% !TeX program = xelatex 
\documentclass[xcolor=svgnames,10pt,UTF8]{ctexbeamer}
%\documentclass[aspectratio=169,xcolor=svgnames,10pt]{beamer}

\RequirePackage{slide}

\logo{\includegraphics[scale=0.02]{../figure/logo.jpg}}
\title{SystemVerilog芯片验证}
\subtitle{第5章~面向对象编程}
%\author{王旭}
%\institute{深圳信息职业技术学院~微电子学院}
%\date{\today}

\begin{document}

\frame{\titlepage}
\LogoOn
\LogoOff

\section{类的定义}\label{sec:class conception}

\begin{frame}[t,fragile]{类的定义}
	\begin{enumerate}
		\item 类（class）是现实世界中一系列特征相似的客观事物（对象）的抽象。
		\item 在程序设计中，类是用于创建对象的蓝图（blueprint），是包含了属性（property）和方法（method）的数据类型。
		\item 方法是类的访问接口，在类的外部通过调用方法访问或修改类中的属性。
		\item 类是一种自动数据类型。
	\end{enumerate}
	请举例说明类和对象的关系。

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-10},consecutivenumbers=false]
{src/ch5/sec1/1/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{在类外定义方法}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-12},consecutivenumbers=false]
{src/ch5/sec1/2/transaction.svh}

\end{frame}

\section{对象、句柄和构造方法}\label{sec object handle and constructor}

\begin{frame}[t,fragile]{在类外定义方法}

	\begin{enumerate}
		\item 类是一种抽象的数据类型，它本身不占据存储空间。
		\item 类的实例被称为对象，对象占据存储空间。
		\item 句柄（handle）是使用类作为数据类型而定义的变量，可以简单认为句柄保存了对象所在的存储空间的起始地址。
		\item 对象不能直接被访问，只能使用句柄间接地进行访问。
	\end{enumerate}

\end{frame}

\subsection{默认构造方法}\label{Default Constructor}

\begin{frame}[t,fragile]{类、句柄和对象的关系}

	\begin{enumerate}
		\item 对象是通过调用类的构造方法（constructor）创建出来的。
		\item 类中名字叫做“new”的方法就是构造方法，每个类都自带一个没有参数的new方法，也被称为默认构造方法。
		\item 构造方法为对象分配存储空间，然后初始化对象中的属性，最后返回对象的地址。对象的地址值通常会赋值给对应的句柄。
	\end{enumerate}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.8]{handle_and_object.pdf}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-13},consecutivenumbers=false]
{src/ch5/sec1/1/test.sv}

\begin{lstlisting}
default: a=0
tr=15554ba48160
\end{lstlisting}

\end{frame}

\subsection{自定义构造方法}\label{Custom Constructor}

\begin{frame}[t,fragile]{自定义构造方法}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-14},consecutivenumbers=false]
{src/ch5/sec2/1/transaction.svh}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={7-13},consecutivenumbers=false]
{src/ch5/sec2/1/test.sv}

\end{frame}

\subsection{句柄}\label{Getting a Handle on Objects}

\begin{frame}[t,fragile]{句柄}

	\begin{enumerate}
		\item 句柄是一个64比特整数变量，保存了对象在存储空间中的起始地址。
		\item 使用句柄访问想要操作的对象。
		\item 没有句柄指向的对象（存储空间）会被自动回收。
	\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-16},consecutivenumbers=false]
{src/ch5/sec2/2/test.sv}

\begin{lstlisting}
tr1: type=class $unit::transaction, width=64, value=2aaab8140160
tr2: type=class $unit::transaction, width=64, value=2aaab81401b0
\end{lstlisting}

\end{frame}

\subsection{对象的传递}\label{Passing Objects and Handles to Methods}

\begin{frame}[t,fragile]{对象的传递：句柄的值传递}

句柄属于简单数据类型，因此会采用值传递的方式向例程传递句柄，即将例程外部的句柄值复制给例程的句柄参数，两个句柄都指向了同一个对象。传递句柄等价于传递对象。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-19},consecutivenumbers=false]
{src/ch5/sec2/3/test.sv}

\begin{lstlisting}
: a=0
: a=f
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{figure}[!htb]
	\centering%\captionsetup{font={small}}\small
	\scalebox{1.0}{
	\begin{tikzpicture}
		\tikzstyle{every node}=[font=\normalsize]
		%\draw[thick,color=gray] (0,0) grid (12,6);
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=2.5cm] (task) at (0,0) {function void rev(\textcolor{red}{input transaction tr});\\ \ \ tr.a = \textasciitilde tr.a; // 使用形参tr访问对象成员 \\endtask};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=2.5cm] (transaction) at (-2,2) {initial begin\\ \ \ transaction \textcolor{red}{tr}; \\ \ \ rev(tr); \\ end};
		\node[thick,draw,color=red,align=left,minimum height=0.8cm,minimum width=2.5cm] (object) at (3,2) {transaction对象};
		\draw [thick,color=red,-Latex] (transaction.east) -- (object.west);
		\draw [thick,color=red,-Latex] (task.north) -- (object.west);
	\end{tikzpicture}
	}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{对象的传递：句柄的引用传递}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-18},consecutivenumbers=false]
{src/ch5/sec2/4/test.sv}

\begin{lstlisting}
: a=1
: a=3
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{figure}[!htb]
	\centering%\captionsetup{font={small}}\small
	\scalebox{0.8}{
	\begin{tikzpicture}
		\tikzstyle{every node}=[font=\normalsize]
		%\draw[thick,color=gray] (0,0) grid (12,6);
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=2.5cm] (task) at (0,0) {function void create(\textcolor{red}{ref transaction tr}); \\ \ \ tr = new();  // 形参tr是实参tr的别名 \\endtask};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=2.5cm] (transaction) at (-2,2) {initial begin\\ \ \ transaction \textcolor{red}{tr}; \\ \ \ ...\\ end};
		\node[thick,draw,color=red,cross out,draw=red,minimum height=0.8cm,minimum width=2.5cm] (object0) at (3,3.5) {旧transaction对象被回收};
		\node[thick,draw,color=red,align=left,minimum height=0.8cm,minimum width=2.5cm] (object1) at (3,2) {新创建的transaction对象};
		\draw [thick,color=red,-Latex] (transaction.east) -- (object1.west);
		\draw [thick,color=red,-Latex] (task.north) -- (object1.west);
	\end{tikzpicture}
	}
\end{figure}

\end{frame}

\subsection{在类中使用句柄}\label{Using Handle in Class}

\begin{frame}[t,fragile]{在类中使用句柄}

在transaction类中定义句柄cfg\_info。配置信息类config\_info中的属性odd\_parity用来保存属性a的奇校验值。

\begin{figure}[!htb]
	\centering\captionsetup{font={footnotesize}}\footnotesize
	\scalebox{0.8}{
	\begin{tikzpicture}
		\tikzstyle{every node}=[font=\normalsize]
		%\draw[thick,color=gray] (0,-2) grid (12,3);
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=2.5cm] (transaction) at (0,0) {class transaction;\\ \ \ bit [3:0] a;\\ \ \ config\_info \textcolor{red}{cfg\_info;}\\ \ \ ... \\endclass};
		\node[thick,draw,color=red,fill=white,align=left,minimum height=1.0cm,minimum width=2.5cm] (config) at (6,0) {class config\_info;\\ \ \ bit odd\_parity; // 保存a的奇校验值\\ \ \ ... \\endclass};
		\draw[thick,color=red,-Latex] (transaction) -- (config);
	\end{tikzpicture}
	}
\end{figure}

类的默认构造方法只能将其内部的所有属性都初始化成默认值，句柄被初始化成默认值null，没有指向任何对象。

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-24},consecutivenumbers=false]
{src/ch5/sec2/5/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{typedef class}

如果config\_info类后被定义，或者它被定义在另一个源文件config\_info.svh中且该文件在transaction.svh之后被导入。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
{src/ch5/sec2/6/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-14},consecutivenumbers=false]
{src/ch5/sec2/6/test.sv}

\begin{lstlisting}
tr1: '{odd_parity:'h1}, a=1
tr2: '{odd_parity:'h0}, a=3
\end{lstlisting}

\end{frame}

\subsection{句柄数组}\label{Arrays of Handles}

\begin{frame}[t,fragile]{句柄数组}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-15},consecutivenumbers=false]
{src/ch5/sec2/7/test.sv}

\begin{lstlisting}
: a=0
: a=1
: a=2
: a=3
\end{lstlisting}

\end{frame}

\section{类的继承}\label{Introduction to Inheritance}

\begin{frame}[t,fragile]{类的继承}

派生类继承了基类的全部成员，同时还可以添加自己的新成员。OOP使用继承特性实现代码重用，继承特性还是实现OOP中多态特性的先决条件。

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.6]{class_inherit.pdf}
\end{figure}

\end{frame}

\subsection{派生类}\label{Extended Classes}

\begin{frame}[t,fragile]{基类trans\_a}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.6]{trans_a_b.pdf}
\end{figure}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-15},consecutivenumbers=false]
{src/ch5/sec3/1/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{派生类trans\_b}

基类和派生类中的print方法是两个普通方法，两者间都没有任何关联。

派生类new方法会自动调用基类new方法。当然也可以手动调用。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={17-30},consecutivenumbers=false]
{src/ch5/sec3/1/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

在介绍虚方法之前，基类对象句柄暂时只能访问派生类中属于基类的那些成员。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-16},consecutivenumbers=false]
{src/ch5/sec3/1/test.sv}

\begin{lstlisting}
create a trans_a object
create a trans_a object
create a trans_b object
tra: a=1
trb: a=2, b=3
: a=2
\end{lstlisting}

\end{frame}

\subsection{虚方法和多态}\label{Virtual Methods}

\begin{frame}[t,fragile]{定义虚方法}
	\begin{enumerate}
		\item 虚方法使用关键字\lstinline[language=SystemVerilog]|virtual|声明，虚方法可以在派生类中进行重定义。
	\end{enumerate}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.6]{class_inherit_virtual.pdf}
	%\caption{在基类中定义虚方法}\label{fig class inherit virtual}
\end{figure}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4,12-17,26-30},consecutivenumbers=false]
{src/ch5/sec3/2/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

	\begin{enumerate}
		\item 使用基类句柄\texttt{tra}指向派生类\texttt{trans\_b}的实例对象。
		\item 在基类\texttt{trans\_a}中声明虚方法\texttt{print}。
		\item 在派生类\texttt{trans\_b}中重定义基类\texttt{trans\_a}中的虚方法\texttt{print}。
	\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-17},consecutivenumbers=false]
{src/ch5/sec3/2/test.sv}

\begin{lstlisting}
create a trans_a object
create a trans_b object
tra: based class, a=1
tra: derived class, b=2
trb: based class, a=1
trb: derived class, b=2
\end{lstlisting}

\end{frame}

\subsection{继承与多态}\label{Inheritance and polymorphism}

\begin{frame}[t,fragile]{继承与多态}

当一个基类句柄指向派生类对象，那么它在调用基类的普通方法和虚方法（派生类重定义了这个虚方法）时会表现出不同的行为，这就是OOP中的多态（polymorphism）特性。引入多态特性后，基类对象句柄可以操作基类的所有派生类对象。

使用对象句柄调用基类的虚方法时，编译器会根据它实际指向的对象类型决定调用基类或派生类中的方法。综上所述，使用对象句柄调用类的方法时遵循如下规则。

\begin{enumerate}
	\item 调用普通方法：根据对象句柄类型（而不是对象句柄所指向的对象类型）调用普通方法。
	\item 调用虚方法：根据对象句柄实际指向的对象类型（而不是对象句柄类型）调用虚方法。当\texttt{tra}指向\texttt{trans\_a}对象时，\texttt{tra}调用基类\texttt{trans\_a}的\texttt{print}虚方法。当\texttt{tra}指向\texttt{trans\_b}对象时，\texttt{tra}调用派生类\texttt{trans\_b}的\texttt{print}虚方法。
\end{enumerate}

\texttt{new}方法不能添加\texttt{virtual}关键字。

\end{frame}

\subsection{抽象类和纯虚方法}\label{Abstract Classes and Pure Virtual Methods}

\begin{frame}[t,fragile]{抽象类和纯虚方法}

	\begin{enumerate}
		\item 抽象类用于高层级建模，它不能例化。
		\item 作为基类为它的所有派生类提供统一的功能接口。
		\item 定义抽象类时需要使用virtual前缀，抽象类中可以定义属性，只能声明纯虚（pure virtual）方法。
		\item 如果派生类中没有重定义抽象类中的全部纯虚方法，那么这个派生类也是一个抽象类。
	\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-6},consecutivenumbers=false]
{src/ch5/sec3/3/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{抽象类}

	抽象类通常用于高层建模，它不能被例化。抽象类只能作为基类使用，为所有派生类提供统一的功能接口。

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.7]{trans_base.pdf}
	%\caption{定义抽象类\texttt{trans\_base}和纯虚方法\texttt{print}}\label{fig class trans base}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{抽象类和纯虚方法}

	抽象类中可以定义属性，也可以声明纯虚（\texttt{pure virtual}）方法，但是不能定义普通方法和虚方法。

	纯虚方法用于建立抽象类的所有派生类的统一功能接口，声明纯虚方法时只需给出方法名、参数类型和返回值类型。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
{src/ch5/sec3/3/transaction.svh}

\end{frame}

\subsection{句柄类型转换}\label{subsec handle casting}

\begin{frame}[t,fragile]{句柄类型转换}

	\begin{enumerate}
		\item 派生类句柄到基类句柄的转换被称为向上类型转换。向上类型转换是安全的。
		\item 基类句柄到派生类句柄的转换被称为向下类型转换。向下类型转换是不安全的。
		\item 使用动态转换函数\$cast尝试向下类型转换。\$cast不仅要检查两个句柄之间的继承关系，还会检查基类句柄实际指向的对象类型。
	\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-27},consecutivenumbers=false]
{src/ch5/sec3/4/test.sv}

\end{frame}

\section{接口类与多继承}\label{sec interface class}

\begin{frame}[t,fragile]{接口类}

	\begin{enumerate}
		\item 接口类（interface class）与接口无任何关系。
		\item 接口类是一种抽象类型，可以把它看成是若干纯虚方法声明的集合。
		\item 接口类中的纯虚方法将在它的派生类中被重新定义。只有接口类支持多继承。
	\end{enumerate}

接口类drivable\_ifc抽象了交通工具的驾驶操作，但是没有指定驾驶哪种交通工具。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-9},consecutivenumbers=false]
{src/ch5/sec4/1/intf_class.svh}

\end{frame}

\begin{frame}[t,fragile]{桥接模式：抽象和实现}

	\begin{enumerate}
		\item 将一个大类拆分为抽象和实现两个独立的层次结构。
		\item 抽象部分就是接口类，它们是一些实体的高阶控制层。该层自身不完成任何具体的工作，它需要将工作委派给实现部分层。
	\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-20},consecutivenumbers=false]
{src/ch5/sec4/1/car.svh}

\end{frame}

\begin{frame}[t,fragile]{驾驶员类}

	\begin{enumerate}
		\item 驾驶员类并不关心它要操作的交通工具对象，它只在虚方法drive中调用交通工具类的各种虚方法，交通工具类可以是轿车类car，也可以是卡车类truck等。
		\item 驾驶员类可以操作不同的交通工具类，这是因为所有的交通工具类都派生自接口类drivable\_ifc，并且重定义了接口类drivable\_ifc的所有纯虚方法。
	\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-17},consecutivenumbers=false]
{src/ch5/sec4/1/driver.svh}

\end{frame}

\begin{frame}[t,fragile]{}

测试模块中创建了car对象和driver对象，并将句柄the\_car传入到驾驶员类driver的new方法。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={8-14},consecutivenumbers=false]
{src/ch5/sec4/1/test.sv}

\begin{lstlisting}
I'm accelerating
I'm turning left
I'm turning right
I'm braking
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{定义第二个接口类}

交通工具还需要购买保险。因为购买保险与驾驶无关，因此有必要再建立一个接口类insurer\_ifc抽象保险费用的计算。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={11-14},consecutivenumbers=false]
{src/ch5/sec4/2/intf_class.svh}

\end{frame}

\begin{frame}[t,fragile]{}

为类car再添加一个接口基类insurer\_ifc，然后重定义其所有纯虚方法。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-22,40-40},consecutivenumbers=false]
{src/ch5/sec4/2/car.svh}

\end{frame}

\begin{frame}[t,fragile]{保险员类}

	\begin{enumerate}
		\item 保险员类并不关心它为哪种交通工具对象计算保险费用，它只在虚方法insure中调用交通工具类的虚方法读取发动机排量和事故数。
		\item 保险员类insurer可以计算各种交通工具对象的保险费用，这是因为所有的交通工具类都派生自接口类insure\_ifc，并且重定义了接口类insure\_ifc的所有纯虚方法。
	\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-16},consecutivenumbers=false]
{src/ch5/sec4/2/insurer.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={9-18},consecutivenumbers=false]
{src/ch5/sec4/2/test.sv}

\begin{lstlisting}
I'm accelerating
I'm turning left
I'm turning right
I'm braking
The insurance is 530
\end{lstlisting}

使用接口类可以将两类不相关的纯虚方法黏合到轿车类car中，在轿车类car中重定义全部的虚方法后，驾驶员对象和保险员对象就可以分别使用这些虚方法。如果没有接口类提供的多继承功能，这种操作是不可能完成的。

\end{frame}

\section{类的封装}\label{sec class encapsulation}

\begin{frame}[t,fragile]{类的封装}

\begin{enumerate}
	\item 类的封装是指将属性和处理属性的方法包围起来，同时使用关键字protected和local设置类中成员在当前类及其派生类中的访问权限。
	\item 方便用户使用类。
	\item 方便设计人员修改类的内部结构。
\end{enumerate}

\begin{table}[!htb]
	\centering%\captionsetup{font={small}}\small %small or scriptsize
	\begin{tabular}{lccc}
		\hline
		\rowcolor{black!10} 关键字 & 类中访问 & 类外访问 & 派生类访问 \\\hline
		public & 支持 & 支持 & 支持 \\\hline
		\lstinline[language=SystemVerilog]|local| & 支持 & 不支持 & 不支持 \\\hline
		\lstinline[language=SystemVerilog]|protected| & 支持 & 不支持 & 支持 \\\hline
	\end{tabular}%
\end{table}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-27},consecutivenumbers=false]
{src/ch5/sec5/1/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-20},consecutivenumbers=false]
{src/ch5/sec5/1/test.sv}

\begin{lstlisting}
: a=0, b=0
: a=1, b=2
\end{lstlisting}

\end{frame}

\section{对象复制}\label{Copying Objects}

\subsection{简单对象复制}\label{Copying an Object with the New Operator}

\begin{frame}[t,fragile]{}

new操作符用于复制不包含句柄的简单对象（浅复制），它不会调用任何构造方法。new操作符为新对象申请存储空间，并且复制现有对象的所有属性。如果对象中包含句柄，new操作符只是复制句柄内容，不会复制句柄所指向的对象。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-16},consecutivenumbers=false]
{src/ch5/sec6/1/test.sv}

\begin{lstlisting}
tr1: '{odd_parity:'h1}, a=1
tr1: '{odd_parity:'h0}, a=1
tr2: '{odd_parity:'h0}, a=1
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{figure}[!htb]
	\centering%\captionsetup{font={small}}
	\scalebox{0.8}{
	\begin{tikzpicture}
		\tikzstyle{every node}=[font=\normalsize]
		%\draw[thick,color=gray] (0,0) grid (10,5);
		\node[thick,align=left,minimum height=1.0cm,minimum width=1cm] at (-2,2) {tr1 = new();};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (tr1) at (0,2) {tr1};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (cfg1) at (3,2) {a=1\\\textcolor{red}{cfg\_info}};
		\node[thick,draw,color=red,align=left,minimum height=1.0cm,minimum width=1cm] (id1) at (6,2) {odd\_parity=0};
		\node[thick,align=left,minimum height=1.0cm,minimum width=1cm] at (-2,0.0) {tr2 = new tr1;};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (tr2) at (0,0) {tr2};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (cfg2) at (3,0) {a=1\\\textcolor{red}{cfg\_info}};
		\draw[thick,-Latex] (tr1) -- (cfg1);
		\draw[thick,color=red,-Latex] (cfg1) -- (id1);
		\draw[thick,-Latex] (tr2) -- (cfg2);
		\draw[thick,color=red,-Latex] (cfg2.east) -- (id1.west);
	\end{tikzpicture}
	}
\end{figure}

\end{frame}

\subsection{复杂对象复制}\label{writing user defined copy function}

\begin{frame}[t,fragile]{复杂对象复制}

复杂对象的复制应该在类中添加复制方法完成深复制。实现对象深复制的关键就是在层级结构中的每个类中都添加相应的copy方法，每个copy方法都可以实现其所在对象的内容复制。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-14},consecutivenumbers=false]
{src/ch5/sec6/2/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={16-33},consecutivenumbers=false]
{src/ch5/sec6/2/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-16},consecutivenumbers=false]
{src/ch5/sec6/2/test.sv}

\begin{figure}[!htb]
	\centering%\captionsetup{font={small}}
	\scalebox{0.7}{
	\begin{tikzpicture}
		\tikzstyle{every node}=[font=\normalsize]
		%\draw[thick,color=gray] (0,0) grid (10,5);
		\node[thick,align=left,minimum height=1.0cm,minimum width=1cm] at (-2,2) {tr1 = new(1);};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (tr1) at (0,2) {tr1};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (cfg1) at (3,2) {a=1\\\textcolor{red}{cfg\_info}};
		\node[thick,draw,color=red,align=left,minimum height=1.0cm,minimum width=1cm] (id1) at (6,2) {odd\_parity=1};
		
		\node[thick,align=left,minimum height=1.0cm,minimum width=1cm] at (-2,0.0) {tr2 = new();};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (new_tr1) at (0,0) {tr2};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (new_cfg2) at (3,0) {a=0\\\textcolor{blue}{cfg\_info}};
		\node[thick,draw,color=blue,align=left,minimum height=1.0cm,minimum width=1cm] (new_id2) at (6,0) {odd\_parity=0};

		\node[thick,align=left,minimum height=1.0cm,minimum width=1cm] at (-2,-2) {tr2.copy(tr1);};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (copy_tr1) at (0,-2) {tr2};
		\node[thick,draw,align=left,minimum height=1.0cm,minimum width=1cm] (copy_cfg2) at (3,-2) {a=1\\\textcolor{blue}{cfg\_info}};
		\node[thick,draw,color=blue,align=left,minimum height=1.0cm,minimum width=1cm] (copy_id2) at (6,-2) {odd\_parity=1};
				
		\draw[thick,-Latex] (tr1) -- (cfg1);
		\draw[thick,color=red,-Latex] (cfg1) -- (id1);
		
		\draw[thick,-Latex] (new_tr1) -- (new_cfg2);
		\draw[thick,color=blue,-Latex] (new_cfg2) -- (new_id2);

		\draw[thick,-Latex] (copy_tr1) -- (copy_cfg2);
		\draw[thick,color=blue,-Latex] (copy_cfg2) -- (copy_id2);
	\end{tikzpicture}
	}
\end{figure}

\end{frame}

\subsection{派生类对象复制}\label{derived object copy Methods}

\begin{frame}[t,fragile]{派生类对象复制}

	\begin{enumerate}
		\item 派生类对象在复制时，不但要复制自身添加的内容，还需要复制从基类继承的内容。
		\item 复制派生类对象的规则是在派生类和它的所有基类中都定义用来复制的copy方法（虚方法），每个派生类中的copy方法先调用其上一级基类的copy方法，再复制自身的内容。
	\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-7},consecutivenumbers=false]
{src/ch5/sec6/3/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={9-26},consecutivenumbers=false]
{src/ch5/sec6/3/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={28-48},consecutivenumbers=false]
{src/ch5/sec6/3/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-16},consecutivenumbers=false]
{src/ch5/sec6/3/test.sv}

\begin{lstlisting}
tr1: a=1
tr1: b=2
before copy, tr2: a=0
before copy, tr2: b=0
after copy, tr2: a=1
after copy, tr2: b=2
\end{lstlisting}

\end{frame}

\section{参数化类}\label{Parameterized Classes}

\subsection{定义参数化类}\label{A Simple stack}

\begin{frame}[t,fragile]{简单堆栈类}
	
\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-14},consecutivenumbers=false]
{src/ch5/sec7/1/stack_int.svh}

\end{frame}

\begin{frame}[t,fragile]{参数化堆栈类}
	
\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-14},consecutivenumbers=false]
{src/ch5/sec7/2/stack.svh}

\end{frame}

\begin{frame}[t,fragile]{参数化类的特殊化}

	使用参数化类定义句柄时，需要为它的数据类型参数指定具体的数据类型。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-16},consecutivenumbers=false]
{src/ch5/sec7/2/test.sv}

\begin{lstlisting}
7.000000
6.000000
5.000000
4.000000
3.000000
2.000000
...
\end{lstlisting}

\end{frame}

\subsection{参数化类的应用}\label{application of parameterized Classes}

\begin{frame}[t,fragile]{参数化类的特殊化}

	测试平台中的序列发生器是一个典型的参数化类。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-11},consecutivenumbers=false]
{src/ch5/sec7/3/sequencer.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={7-17},consecutivenumbers=false]
{src/ch5/sec7/3/test.sv}

\begin{lstlisting}
seqr: a=0
seqr: a=0
seqr: b=0
\end{lstlisting}

\end{frame}

\subsection{共享参数化类}\label{Sharing Parameterized Classes}

\begin{frame}[t,fragile]{共享参数化类}

参数化类被多次特殊化后会出现很多相同类型的句柄，但是这些句柄所指向的对象只能处理在特殊化时所指定的数据类型。为了方便地操作所有特殊化后的句柄，可以为参数化类定义一个抽象基类，并将参数化类中的方法以纯虚方法的形式定义在抽象基类中。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-15},consecutivenumbers=false]
{src/ch5/sec7/4/sequencer.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={7-20},consecutivenumbers=false]
{src/ch5/sec7/4/test.sv}

\begin{lstlisting}
seqr: a=0
seqr: a=0
seqr: b=0
\end{lstlisting}

\end{frame}

\section{静态属性和静态方法}\label{Static Variables vs. Global Variables}

\subsection{静态属性}\label{A Simple Static Variable}

\begin{frame}[t,fragile]{静态属性}

静态属性使用static关键字定义，它可以被类的所有实例共享。静态属性在代码的编译阶段完成初始化，因此静态属性通常在定义时就给定其初值。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-16},consecutivenumbers=false]
{src/ch5/sec8/1/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.8]{static_properity.pdf}
\end{figure}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-15},consecutivenumbers=false]
{src/ch5/sec8/1/test.sv}

\begin{lstlisting}
amount:0
amount:0
amount:1
amount:1
\end{lstlisting}

\end{frame}

\subsection{静态句柄}\label{A Static Handle}

\begin{frame}[t,fragile]{静态句柄}

将类中的所有静态属性封装在一个新类中，然后在当前类中使用一个静态句柄管理这个新类。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-20},consecutivenumbers=false]
{src/ch5/sec8/2/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-13},consecutivenumbers=false]
{src/ch5/sec8/2/test.sv}

\begin{lstlisting}
'{amount:'h0}
'{amount:'h1}
\end{lstlisting}

\end{frame}

\subsection{静态方法}\label{Ch05 Static Methods}

\begin{frame}[t,fragile]{静态方法}

为了方便地操作类中的静态属性，可以在类中定义静态方法。静态方法只能操作静态属性，不能操作自动属性。和静态属性一样，静态方法在对象例化前就已经存在，因此在编译阶段可以使用类的静态方法访问类的静态属性。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-10,21-24},consecutivenumbers=false]
{src/ch5/sec8/3/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{静态方法}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-13},consecutivenumbers=false]
{src/ch5/sec8/3/test.sv}

\end{frame}

\section{单例类}\label{Static and Singleton Classes}

\subsection{打印信息的动态类}\label{Dynamic Class to Print Messages}

\begin{frame}[t,fragile]{单例类}

	\begin{enumerate}
		\item 静态属性和静态方法可以直接访问，与它们所在的类是否被实例化没有关系。
		\item 类如果没有被实例化，就不能使用句柄传递，如果被实例化多次，又会造成存储空间的浪费。
		\item 单例类（singleton class）是指一个只能被例化一次的类。
	\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{打印服务类}

	print类在测试平台的多个组件中被使用，它会被例化多次，占用大量的存储空间，这是动态print类的缺点。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-21},consecutivenumbers=false]
{src/ch5/sec9/1/print.svh}

\end{frame}

\begin{frame}[t,fragile]{优化打印服务类}

为了减少存储空间占用，可以将print类中的属性和方法修改成静态类型。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-15},consecutivenumbers=false]
{src/ch5/sec9/2/print.svh}

\end{frame}

\subsection{打印信息的单例类}\label{Singleton Class to Print Messages}

\begin{frame}[t,fragile]{单例类}

单例类是一个只能被例化一次的类。单例类既可以简化程序结构，又不会占用大量存储空间。类中定义了用来创建唯一对象的静态方法get\_object，当类没有实例时这个方法会创建一个对象，如果类已经存在一个实例，它只返回指向对象的句柄。另外为了确保singleton类不能以其他方式被例化，必须将它的构造方法声明成protected（不要将构造方法声明成local，因为派生类可能需要访问基类的构造方法）。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-15},consecutivenumbers=false]
{src/ch5/sec9/3/singleton.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-16},consecutivenumbers=false]
{src/ch5/sec9/3/test.sv}

\begin{lstlisting}
46912721125728, 46912721125728
Singleton class
\end{lstlisting}

\end{frame}

\section{UVM配置数据库}\label{sec Configuration Database Class}

\begin{frame}[t,fragile]{UVM配置数据库（configuration database）}

静态属性、静态方法和参数化类的重要应用是建立全局性质的配置数据库。仿真开始前测试平台的多个组件都需要使用配置数据库进行随机配置，配置数据库应该具有如下性质。

\begin{enumerate}
	\item 配置数据库应该定义在任何块之外，具有全局性质，在测试平台的任何位置都可以被访问。
	\item 支持各种简单和复杂的数据类型（例如bit、int、interface、对象等）的访问。
	\item 支持使用索引查询配置数据库中的数据。
\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{整数配置数据库类}

\begin{lstlisting}[language=SystemVerilog,numbers=left,consecutivenumbers=false]
class config_db_int;
	static int db[string];

	static function void set(input string name, input int value);
		db[name] = value;
	endfunction

	static function void get(input string name, ref int value);
		value = db[name];
	endfunction

	static function void dump();
		$display("configuration database %s", $typename(int));
		foreach (db[i])
			$display("db[%s]=%p", i, db[i]);
	endfunction
endclass
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{整数配置数据库类}

类成员的功能描述如下。

\begin{enumerate}
	\item 使用一个字符串作为索引的关联数组db存储整数配置数据。
	\item set方法按照索引值name将整数值value保存在配置数据库中，set方法类似于寄信。
	\item get方法按照索引值name从配置数据库中取出整数值value，get方法类似于收信。
	\item print方法用来打印配置数据库中的全部内容。
\end{enumerate}

配置数据库机制用于在测试平台内部传递各种数据。因此set方法和get方法通常都是成对出现的。系统函数\$typename以字符串的形式返回参数的解析类型。类中的所有成员都被声明成static，这样配置数据库在全局作用域内都可见，静态属性和静态方法可以通过类名直接访问。

\end{frame}

\begin{frame}[t,fragile]{参数化的配置数据库类svm\_config\_db}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-19},consecutivenumbers=false]
{src/svm-1.1/svm_config_db.svh}

\end{frame}

\begin{frame}[t,fragile]{svm\_config\_db的使用}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={7-22},consecutivenumbers=false]
{src/ch5/sec10/1/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={24-31},consecutivenumbers=false]
	{src/ch5/sec10/1/test.sv}

\begin{lstlisting}
configuration database int
db[high]=99
db[low]=0
configuration database real
db[pi]=3.14
configuration database class $unit::config_info
db[cfg]='{amount:'h8}
i is configured to 99
j is configured to 3.140000
k is configured to '{amount:'h8}
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{UVM配置数据库}

UVM测试平台采用树形结构进行组织和管理，其中每个节点代表一个组件（\texttt{uvm\_component}的派生类）对象。组件对象使用一条绝对路径信息记录它在树形结构中的位置。由于\texttt{set}和\texttt{get}方法的调用位置不固定，因此它们使用对象句柄\texttt{cntxt}指向一个节点对象，再使用\texttt{inst\_name}描述该节点下面的相对路径，这2个参数联合起来组成一个节点的绝对路径。\texttt{set}方法使用这个路径控制数据在树形结构中的可见性，\texttt{get}方法使用这个路径设置在哪个节点得到数据。

\begin{lstlisting}[language=SystemVerilog,numbers=left,consecutivenumbers=false]
static function void set(uvm_component cntxt, string inst_name,
	string field_name, T value);

static function void get(uvm_component cntxt, string inst_name,
	string field_name, T value);
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{uvm\_config\_db的使用}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-20},consecutivenumbers=false]
{src/ch5/sec10/2/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={22-31},consecutivenumbers=false]
	{src/ch5/sec10/2/test.sv}

\end{frame}

\section{UVM工厂机制}\label{Creating a Test Registry}

\subsection{使用静态方法的测试登记表}\label{Test registry with Static Methods}

\begin{frame}[t,fragile]{使用静态方法的测试登记表}

	减少编译和仿真时间。

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.8]{one_test_in_top_tb.pdf}
\end{figure}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.8]{vmm_test_registry.pdf}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{测试基类}

为了便于统一管理所有的测试类，首先需要编写一个测试基类，再从这个测试基类派生出所有的测试类。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-7},consecutivenumbers=false]
{src/ch5/sec11/1/tests/base_test.svh}

\end{frame}

\begin{frame}[t,fragile]{环境类environment}

test\_base中使用的环境类environment。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-16},consecutivenumbers=false]
{src/ch5/sec11/1/environment.svh}

\end{frame}

\begin{frame}[t,fragile]{测试登记表类test\_registry}

\begin{enumerate}
	\item 静态属性registry。
	\item 静态方法register。
	\item 静态方法get\_test。
\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-17},consecutivenumbers=false]
{src/ch5/sec11/1/test_registry.svh}

\end{frame}

\begin{frame}[t,fragile]{}

所有的测试对象都应该在测试启动前被自动例化出来，并注册到测试登记表中，这样测试平台只需关注运行哪个测试对象就可以了。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
{src/ch5/sec11/1/tests/test0.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
{src/ch5/sec11/1/tests/test1.svh}

\end{frame}

\begin{frame}[t,fragile]{}

在测试平台中导入所有测试类源文件，这样所有全局测试对象都在测试启动前被创建出来，且都被注册到测试登记表中。

测试平台调用测试登记表类中的get\_test方法从命令行读入测试登记表的索引名，然后从测试登记表中选出对应的测试对象，最后运行测试对象的run\_test方法。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={1-13},consecutivenumbers=false]
{src/ch5/sec11/1/top_tb.sv}

\end{frame}

\begin{frame}[t,fragile]{运行结果}

\begin{lstlisting}
$unit::\test_simple::run_test 
call env's build
call env's connect
call env's main
\end{lstlisting}

\begin{lstlisting}
$unit::\test_bad::run_test 
call env's build
call env's connect
call env's main
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{静态方法的测试登记表的缺点}

\begin{enumerate}
	\item 在测试启动前创建所有的全局测试对象，这会占用太多的内存空间。
	\item 测试启动后测试平台无法修改测试类的内容，例如将测试类中的环境类environment替换成其他环境类。
\end{enumerate}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.8]{ovm_cake.pdf}
\end{figure}

\end{frame}

\subsection{工厂机制}\label{subsec factory mechanism}

\begin{frame}[t,fragile]{蛋糕的工厂机制}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.6]{factory_cake.pdf}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{UVM的工厂机制}

	UVM的工厂机制本质上就是一个关联数组，将各个具有继承关系的类联系到一起形成一张表格。在创建的时候，可以通过查表来实现最终类型的例化。

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.6]{registry_and_factory.pdf}
\end{figure}

\end{frame}

\subsection{组件基类}\label{subsec component based class}

\begin{frame}[t,fragile]{UVM类继承关系}

测试平台由从组件基类\texttt{uvm\_component}派生的各种组件对象构成。当一个组件对象被创建后，它就成为测试平台层次结构中的一部分，组件对象在仿真期间一直存在。使用序列类（\texttt{uvm\_sequence}）创建出来的事务对象都是临时的，即它们在使用后会被销毁。

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.7]{uvm-inheritance.pdf}
	%\caption{简化的UVM类继承关系}\label{uvm inheritance}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{组件基类和组件类}

所有组件类和测试类的公共抽象基类svm\_object。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-5},consecutivenumbers=false]
{src/svm-1.1/svm_component.svh}

组件类是svm\_object类的派生类。它用来模拟UVM库中的uvm\_component，这里规定所有的组件类和测试类都从svm\_object派生。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={7-17},consecutivenumbers=false]
{src/svm-1.1/svm_component.svh}

\end{frame}

\subsection{代理类}\label{subsec proxy class}

\begin{frame}[t,fragile]{所有代理类的公共抽象基类svm\_object\_wrapper}

公共抽象基类svm\_object\_wrapper包含以下两个虚方法。

\begin{enumerate}
	\item 虚方法create\_component只返回一个空句柄，在派生代理类中它根据类型名创建对应的测试对象并返回测试对象的句柄。
	\item 纯虚方法get\_type\_name没有内容，在派生代理类中它的功能是以字符串的形式返回类的类型名。
\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-9},consecutivenumbers=false]
{src/svm-1.1/svm_factory.svh}

\end{frame}

\begin{frame}[t,fragile]{组件代理类svm\_component\_registry}

svm\_component\_registry是svm\_object\_wrapper的派生类，参数T代表测试类的类型，Tname代表测试类的类型名。

svm\_component\_registry类是一个单例类，在工厂中使用create\_component方法创建测试实例。组件代理类的create方法用于创建组件实例。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
{src/svm-1.1/svm_registry.svh}

\end{frame}

\begin{frame}[t,fragile]{}

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={20-41},consecutivenumbers=false]
	{src/svm-1.1/svm_registry.svh}
	
\end{frame}

\begin{frame}[t,fragile]{定义svm\_component\_utils宏}
	
为了方便在组件类和测试类中加入代理类。

\begin{enumerate}
	\item type\_id：使用typedef语句将特殊化后的代理类命名成type\_id。
	\item 虚方法get\_type\_name：这个虚方法使用语法\verb|`"T`"|将符号T中保存的类名转换成字符串并返回。本例中暂时没有使用这个方法。
\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={1-5},consecutivenumbers=false]
{src/svm-1.1/svm_object_defines.svh}

`svm\_component\_utils(test\_simple)的展开内容。

\begin{lstlisting}[language=SystemVerilog,numbers=left,consecutivenumbers=false]
typedef svm_component_registry #(test_simple, "test_simple") type_id;
virtual function string get_type_name ();
	return "test_simple";
endfunction
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{代理类type\_id的初始化过程}

在仿真开始前完成代理类的内部静态句柄me的初始化。

\begin{enumerate}
	\item 在每个测试类中都加入svm\_component\_utils宏，宏的参数为测试类自身。这个宏将参数化代理类svm\_component\_registry特殊化为测试类类型的代理类type\_id。这样每个测试类中都会包含一个对应的代理类type\_id。
	\item 代理类type\_id中包含一个静态句柄me，编译器会在仿真开始前调用静态方法get初始化静态句柄me，这里给me赋值的目的就是让编译器在仿真前调用静态方法get。
	\item 如果静态句柄me内容为null，静态方法get首先会创建工厂类（单例类）的唯一实例。然后调用new方法让静态句柄me指向代理类type\_id的唯一实例。最后调用工厂的register方法，将句柄me（指向代理对象）保存到工厂中的关联数组m\_type\_names。
	\item 如果静态句柄me不等于null，说明代理对象已经被注册过，这时静态方法get直接返回句柄me。
\end{enumerate}

\end{frame}

\subsection{工厂类}\label{subsec factory class}

\begin{frame}[t,fragile]{工厂类svm\_factory也是一个单例类}

关联数组m\_type\_names保存了所有指向代理对象的句柄。

在get\_test方法中，命令行传入的参数保存在字符串name中，name作为索引从关联数组m\_type\_names中找到对应的代理类对象并赋值给句柄test\_wrapper，最后调用代理对象的create\_component方法创建测试实例。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={11-22},consecutivenumbers=false]
{src/svm-1.1/svm_factory.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={24-43},consecutivenumbers=false]
{src/svm-1.1/svm_factory.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={44-59},consecutivenumbers=false]
{src/svm-1.1/svm_factory.svh}

\end{frame}

\begin{frame}[t,fragile]{工厂类的重载}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.6]{factory_override.pdf}
\end{figure}

\end{frame}

\subsection{封装工厂机制}\label{subsec factory class package}

\begin{frame}[t,fragile]{封装工厂机制}
	
将上述所有相关的源文件封装在一个名字为svm\_pkg的包中。导入svm\_pkg包，就相当于为测试平台加入了如下几种机制。

\begin{enumerate}
	\item 配置数据库机制。
	\item 代理机制。
	\item 工厂机制（支持类型重载功能）。
\end{enumerate}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-10},consecutivenumbers=false]
{src/svm-1.1/svm_pkg.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{figure}[!htb]
	\centering%\captionsetup{font={small}} %small or scriptsize
	\begin{forest}
		pic dir tree,
		where level=0{}{% folder icons by default; override using file for file icons
			directory,
		},
		[src
			[svm-1.1]
			[svm-1.2]
			[Makefile, file]
			[setting.sh, file]
		]
	\end{forest}
\end{figure}

\end{frame}

\subsection{UVM-1.2中的工厂机制}\label{subsec uvm 1.2}

\begin{frame}[t,fragile]{核心服务类}

UVM-1.2新增加了uvm\_coreservice\_t和uvm\_default\_coreservice\_t。
uvm\_coreservice\_t使用自身的静态方法get创建uvm\_default\_coreservice\_t的唯一实例。

\begin{lstlisting}[language=SystemVerilog,numbers=left,linerange={1-16},consecutivenumbers=false]
`ifndef UVM_CORESERVICE_TYPE
`define UVM_CORESERVICE_TYPE uvm_default_coreservice_t
`endif

typedef class `UVM_CORESERVICE_TYPE;

virtual class uvm_coreservice_t;
	// 省略纯虚方法声明
	local static `UVM_CORESERVICE_TYPE inst;
	
	static function uvm_coreservice_t get();
		if(inst==null)
			inst=new;
		return inst;
	endfunction
endclass

class uvm_default_coreservice_t extends uvm_coreservice_t;
	local uvm_factory factory;
	local uvm_tr_database tr_database;
	local uvm_report_server report_server;
	local uvm_visitor#(uvm_component) _visitor;
	// 省略纯虚方法的重定义
endclass
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{}

核心服务类用于统一管理UVM中常用的4个单例类。

\begin{enumerate}
	\item 单例类uvm\_factory，用来注册、重载和例化各种组件。
	\item 单例类uvm\_report\_server，用来做消息统筹和报告。
	\item 单例类uvm\_tr\_database，用来记录事务对象。
	\item 单例类uvm\_visitor，主要用来检查组件对象名。
\end{enumerate}

\begin{lstlisting}[language=SystemVerilog,numbers=left,linerange={18-24},consecutivenumbers=false]
`ifndef UVM_CORESERVICE_TYPE
`define UVM_CORESERVICE_TYPE uvm_default_coreservice_t
`endif

typedef class `UVM_CORESERVICE_TYPE;

virtual class uvm_coreservice_t;
	// 省略纯虚方法声明
	local static `UVM_CORESERVICE_TYPE inst;
	
	static function uvm_coreservice_t get();
		if(inst==null)
			inst=new;
		return inst;
	endfunction
endclass

class uvm_default_coreservice_t extends uvm_coreservice_t;
	local uvm_factory factory;
	local uvm_tr_database tr_database;
	local uvm_report_server report_server;
	local uvm_visitor#(uvm_component) _visitor;
	// 省略纯虚方法的重定义
endclass
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{简化的核心服务类svm\_coreservice\_t}

svm\_coreservice\_t是单例类，它只管理自身和工厂类svm\_factory，其中get方法用于创建并获取自身的唯一实例，get\_factory方法用于创建并获取工厂类的唯一实例。在UVM-1.2中，组件类中工厂实例的获取不再使用svm\_factory的get方法，而是使用svm\_coreservice\_t的get\_factory方法。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-8,17-27},consecutivenumbers=false]
{src/svm-1.2/svm_coreservice.svh}

\end{frame}

\begin{frame}[t,fragile]{}

为了兼容UVM-1.1，svm\_factory的get方法被保留下来，但是它不再直接调用自身的new方法，而是调用svm\_coreservice\_t的get\_factory方法创建工厂类的唯一实例。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={16-20},consecutivenumbers=false]
{src/svm-1.2/svm_factory.svh}

\end{frame}

\begin{frame}[t,fragile]{}

组件代理类svm\_component\_registry的get方法使用工厂注册自身的唯一实例，get方法中工厂实例的获取同样也被修改成调用svm\_coreservice\_t的get\_factory方法。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={24-33},consecutivenumbers=false]
{src/svm-1.2/svm_registry.svh}

\end{frame}

\begin{frame}[t,fragile]{}

最后svm\_component\_utils宏的内容稍有改变，它被拆分成m\_svm\_component\_registry\_internal和m\_svm\_get\_type\_name\_func宏。

\lstinputlisting[language=SystemVerilog,numbers=left,consecutivenumbers=false]
{src/svm-1.2/svm_object_defines.svh}

\end{frame}

\subsection{组件类加入工厂机制}\label{subsec add factory to component class}

\begin{frame}[t,fragile]{组件类加入工厂机制}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-23},consecutivenumbers=false]
{src/ch5/sec11/2/ori_env.svh}

\end{frame}

\begin{frame}[t,fragile]{组件类加入工厂机制}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={25-44},consecutivenumbers=false]
{src/ch5/sec11/2/sub_env.svh}

\end{frame}

\begin{frame}[t,fragile]{重载功能}

重载是工厂机制的重要功能，使用重载功能必须满足如下条件。

\begin{enumerate}
	\item 新的用于替换的类型必须继承于原有类型。即environment\_bad是environment的派生类。
	\item 无论是用于重载的类（environment\_bad）还是被重载的类（environment），都要在定义的时候使svm\_component\_utils宏加入工厂机制。
	\item 被重载的类在例化时，必须使用代理对象的create方法，而不能使用类自带的new方法。
	\item 在被重载的类（environment）中，需要被重载的方法必须定义为virtual类型，否则基类句柄无法访问派生类中的这个被重载的方法。
\end{enumerate}

\end{frame}

\subsection{测试类加入工厂机制}\label{subsec add factory to test class}

\begin{frame}[t,fragile]{测试类test\_simple}
	
例化environment时使用new方法，不支持工厂机制。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-22},consecutivenumbers=false]
{src/ch5/sec11/2/tests/test0.svh}

\end{frame}

\begin{frame}[t,fragile]{测试类test\_bad}

例化environment时使用了代理类的create方法，支持工厂机制的重载功能。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-22},consecutivenumbers=false]
{src/ch5/sec11/2/tests/test1.svh}

\end{frame}

\begin{frame}[t,fragile]{重载功能的具体执行过程是}

\begin{enumerate}
	\item 在test\_bad的new方法中使用工厂类的类型重载方法override\_type，将组件类environment\_bad的代理对象的索引值“environment\_bad”注册到工厂中的重载关联数组override中，使用的索引值是“environment”。
	\item 在test\_bad中使用environment类的代理对象的create方法创建对象实例，create方法实际上是调用工厂类的create\_object\_by\_type方法。
	\item 工厂类的create\_object\_by\_type方法会调用find\_override方法。后者使用索引值“environment”将关联数组override中的元素值“environment\_bad”找出。使用“environment\_bad”作为索引在工厂类的代理对象登记表m\_type\_names中找出组件类environment\_bad的代理对象句柄，这个句柄就是find\_override方法的返回值。
	\item 在create\_object\_by\_type方法中，find\_override方法的返回值被赋值给代理对象句柄proxy。现在proxy指向了环境类environment\_bad的代理对象。因此使用proxy的create\_component方法将会创建出环境对象environment\_bad。这样在测试类test\_bad中就实现了环境类environment的重载。
\end{enumerate}

\end{frame}

\subsection{修改测试平台}\label{subsec modify testbench for factory mechanism}

\begin{frame}[t,fragile]{修改测试平台}

首先使用include语句导入所有的测试类文件，然后在initial过程中创建一个test\_obj句柄，再调用工厂类的get\_test方法创建的一个测试对象并赋值给句柄test\_obj，最后调用测试对象的run\_test方法。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={3-14},consecutivenumbers=false]
{src/ch5/sec11/2/top_tb.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{enumerate}
	\item test\_simple类中的svm\_component\_utils宏将特殊化后的参数化代理类svm\_component\_registry定义为单例类type\_id。type\_id中的静态代理类句柄me会在仿真开始前被初始化，即调用svm\_factory::get方法。get方法会创建单例类type\_id的唯一实例，并将其注册到工厂对象的登记表（svm\_factory::m\_type\_names）中。同理，所有测试类都会使用svm\_component\_utils宏将自身的代理对象注册到工厂的登记表中。这样工厂就可以根据需求挑选登记表中的一个type\_id实例创建测试对象。
	\item 仿真开始后，测试模块调用工厂类svm\_factory的get\_test方法从命令行读取测试名。测试名被当做登记表（m\_type\_names）的索引，用来查找匹配的注册对象的句柄（type\_id）。并调用type\_id的create\_object方法创建一个测试类test\_simple的实例。
	\item 测试平台调用测试对象的run\_test方法，这个方法按步骤调用测试对象的build、connect和main方法。
\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{lstlisting}
Create a unique object of singleton class svm_factory
Create a unique object of singleton class svm_component_registry#(environment,environment)
Create a unique object of singleton class svm_component_registry#(environment_bad,environment_bad)
Create a unique object of singleton class svm_component_registry#(test_simple,test_simple)
Create a unique object of singleton class svm_component_registry#(test_bad,test_bad)
svm_pkg::\svm_factory::get_test  found +SVM_TESTNAME=test_simple
svm_pkg::\svm_component::new 
top_tb.\test_simple::new  SVM_TESTNAME=test_simple
svm_pkg::\svm_component::new 
top_tb.\environment::new env
top_tb.\test_simple::run_test 
call env's build
call env's connect
call env's main
$finish called from file "../top_tb.sv", line 13.
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{lstlisting}
Create a unique object of singleton class svm_factory
Create a unique object of singleton class svm_component_registry#(environment,environment)
Create a unique object of singleton class svm_component_registry#(environment_bad,environment_bad)
Create a unique object of singleton class svm_component_registry#(test_simple,test_simple)
Create a unique object of singleton class svm_component_registry#(test_bad,test_bad)
svm_pkg::\svm_factory::get_test  found +SVM_TESTNAME=test_bad
svm_pkg::\svm_component::new 
top_tb.\test_bad::new  SVM_TESTNAME=test_bad
svm_pkg::\svm_component::new 
top_tb.\environment::new env
top_tb.\environment_bad::new env
top_tb.\test_bad::run_test 
call env_bad's build
call env_bad's connect
call env_bad's main
$finish called from file "../top_tb.sv", line 13.
\end{lstlisting}

\end{frame}

\subsection{UVM工厂制造}\label{UVM Factory Build}

\begin{frame}[t,fragile]{UVM工厂制造}

\begin{figure}[!htb]
	\centering%\captionsetup{font={small}} %small or scriptsize
	\scalebox{0.8}{
	\begin{tikzpicture}[parent anchor=south,child anchor=north,level distance=2cm, sibling distance=3.0cm, every node/.style=draw, thick, -Latex, align=center, color=blue]
		\node {uvm\_test\_top \\ (environment)}
			child {node {iagt \\ (agent)}
				child {node {sqr \\ (sequencer)}}
				child {node {drv \\ (driver)}}
				child {node {imon \\ (monitor)}}
			}
			child {node {scb \\ (scoreboard)}}
			child {node {oagt \\ (agent)}
				child {node {omon \\ (monitor)}}
			};
	\end{tikzpicture}
	}
\end{figure}

\begin{lstlisting}[language=SystemVerilog,numbers=left,consecutivenumbers=false]
iagt = agent::type_id::create("iagt", this);
oagt = agent::type_id::create("oagt", this);
scb = scoreboard::type_id::create("scb", this);
\end{lstlisting}

\end{frame}

\section{虚接口}\label{Advanced Interfaces}

\begin{frame}[t,fragile]{虚接口}

模块、接口和包都属于模块类别，因此接口默认是静态类型。类中不能直接定义接口，但是可以使用自动类型的虚接口（virtual interface）间接访问类外（通常位于顶层模块）的接口实例。

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.8]{virtual_interface.pdf}
\end{figure}

\end{frame}

\subsection{使用例程参数连接接口}\label{Using Virtual Interface}

\begin{frame}[t,fragile]{使用例程参数连接接口}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-16},consecutivenumbers=false]
{src/ch5/sec12/1/driver.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-13},consecutivenumbers=false]
{src/ch5/sec12/1/test.sv}

\end{frame}

\subsection{使用跨模块引用连接接口}\label{Cross Module Reference}

\begin{frame}[t,fragile]{使用跨模块引用连接接口}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-14},consecutivenumbers=false]
{src/ch5/sec12/2/test.sv}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-6,35-36},consecutivenumbers=false]
{src/ch5/sec12/2/top_tb.sv}

\end{frame}

\subsection{使用config\_db连接接口}\label{connect if to vif}

\begin{frame}[t,fragile]{使用config\_db连接接口}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={1-1,7-8,23-26,43-43},consecutivenumbers=false]
{src/ch5/sec12/3/top_tb.sv}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-10,15-17},consecutivenumbers=false]
{src/ch5/sec12/3/driver.svh}

\end{frame}

\begin{frame}[t,fragile]{全局配置数据库大大简化了接口的连接}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-13},consecutivenumbers=false]
{src/ch5/sec12/3/test.sv}

\end{frame}

\end{document}
